package com.datatest.config;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author lxt
 * Created by Jccfc on 2021/11/1.
 */
public class RestTemplateConfig {
    @Autowired
    private RestTemplateInterceptor restTemplateInterceptor;
    @Value("${http.max-total}")
    private Integer maxTotal;
    @Value("${http.max-perroute}")
    private Integer maxPerRoute;
    @Value("${http.connection-request-timeout}")
    private Integer connectionRequestTimeout;
    @Value("${http.socket-timeout}")
    private Integer socketTimeout;
    @Value("${http.connect-timeout}")
    private Integer connectTimeout;
    @Value("${http.connetion-time-tolive}")
    private Integer conTimeToLive;
    @Value("${http.max-idle-time}")
    private Integer maxIdleTime;

    /**
     * 自定义RestTemplate配置
     * */
    @Bean
    public RestTemplate restTemplate() {
        //添加连接池
        RestTemplate restTemplate = new RestTemplate(httpRequestFactory());
        //添加RestTemplate的拦截器
        restTemplate.getInterceptors().add(restTemplateInterceptor);
        //添加所有类型数据的转换器
        List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> httpMessageConverter : converters) {
            if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
                ArrayList<MediaType> mediaTypes = new ArrayList<>();
                mediaTypes.add(MediaType.ALL);
                ((MappingJackson2HttpMessageConverter) httpMessageConverter).setSupportedMediaTypes(mediaTypes);
            }
        }
        return restTemplate;
    }

    /**
     * RestTemplate请求连接池的设置并引用HttpClient请求连接池的设置
     * */
    private ClientHttpRequestFactory httpRequestFactory() {

        //创建连接
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(HttpClient());
        // 连接超时
        clientHttpRequestFactory.setConnectTimeout(connectTimeout);
        // 数据读取超时时间，即SocketTimeout
        clientHttpRequestFactory.setReadTimeout(socketTimeout);
        // 从连接池获取请求连接的超时时间，不宜过长，必须设置，比如连接不够用时，时间过长将是灾难性的
        clientHttpRequestFactory.setConnectionRequestTimeout(connectionRequestTimeout);
        return clientHttpRequestFactory;
    }

    /**
     * HttpClient请求连接池的设置
     * */
    private HttpClient HttpClient() {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        //注册协议
        Registry registry = RegistryBuilder.create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        // 设置整个连接池最大连接数 根据自己的场景决定
        connectionManager.setMaxTotal(maxTotal);
        // 路由是对maxTotal的细分
        connectionManager.setDefaultMaxPerRoute(maxPerRoute);
        connectionManager.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
        RequestConfig requestConfig = RequestConfig.custom()
                // 服务器返回数据(response)的时间，超过该时间抛出read timeout
                .setSocketTimeout(socketTimeout)
                // 连接上服务器(握手成功)的时间，超出该时间抛出connect timeout
                .setConnectTimeout(connectTimeout)
                // 从连接池中获取连接的超时时间，超过该时间未拿到可用连接，
                // 会抛出org.apache.http.conn.ConnectionPoolTimeoutException:
                // Timeout waiting for connection from pool
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .build();
        httpClientBuilder.setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .setConnectionManagerShared(true)
                //设置连接池中连接存活时间，默认-1代表无限存活，连接使用之后由response header "Keep-Alive: timeout"决定
                .setConnectionTimeToLive(conTimeToLive, TimeUnit.MILLISECONDS)
//                .setConnectionTimeToLive(-1, TimeUnit.MILLISECONDS)
                //开启空闲连接清理线程，设置连接池中连接最大空闲时间，以及连接清理线程的sleep时间，默认为10秒
                .evictIdleConnections(maxIdleTime, TimeUnit.MILLISECONDS)
                //开启过期连接清理线程，过期时间默认为-1，连接使用后由response header "Keep-Alive: timeout"决定
                .evictExpiredConnections();
        return httpClientBuilder.build();
    }
}
